package com.example.auth.config;

import com.example.auth.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.Arrays;

@Configuration
// 表示当前类是在oauth2标准下
// 实现授权服务器的配置类,启动授权服务器相关功能
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    // 添加依赖注入对象
    // 授权管理器
    @Autowired
    private AuthenticationManager authenticationManager;

    // 登录用户详情类
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    // 添加保存令牌配置的对象
    @Autowired
    private TokenStore tokenStore;

    // 添加客户端详情对象(系统自动提供的)
    @Autowired
    private ClientDetailsService clientDetailsService;

    // 配置授权服务器的各种参数

    /**
     *
     * @param endpoints 端点 (oauth提供的各种控制器方法)
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // Oauth2标准提供了很多控制器方法用于授权或认证
        // 这个参数endpoints翻译为断电,这里指的就是Oauth2提供的控制器方法
        // 这个方法要做的就是配置端点,以便授权和验证等操作顺利执行
        endpoints
                // 配置授权管理器对象
                .authenticationManager(authenticationManager)
                // 配置登陆时调用的获得用户详情的对象
                .userDetailsService(userDetailsService)
                // 配置登陆信息提交只能是post
                .allowedTokenEndpointRequestMethods(HttpMethod.POST)
                // 配置生成令牌的对象
                .tokenServices(tokenService());
    }




    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;
    /**
     * 配置生成令牌和保存令牌的方法 (Spring容器也会调用)
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        // 创建生成令牌的对象
        DefaultTokenServices services = new DefaultTokenServices();
        // 设置令牌如何保存
        services.setTokenStore(tokenStore);
        // 新增代码
        // 实例化令牌增强对象
        TokenEnhancerChain chain = new TokenEnhancerChain();
        // 设置JWT转换器对象到令牌增强对象中
        chain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        // 将令牌增强其对象,加载到生成令牌的对象中
        services.setTokenEnhancer(chain);
        // 设置令牌有效期(单位是秒)
        services.setAccessTokenValiditySeconds(3600);
        // 配置这个令牌为那个客户端生成
        services.setClientDetailsService(clientDetailsService);
        return services;
    }

    // 获取加密对象
    @Autowired
    private PasswordEncoder passwordEncoder;
    /**
     * 重写方法2 配置客户端对应的各种权限
     * 这里的客户端指所有依赖当前授权服务器进行授权和登录的项目
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 当前项目只支持springcloud进行授权
        // "所有客户端",只包含当前项目
        clients.inMemory() // 将客户端信息保存在内存中(如王者,腾讯视频等项目请求当前的授权服务器)
                .withClient("springcloud") // 开始配置springcloud项目的权限
                .secret(passwordEncoder.encode("123456")) // springcloud客户端密码
                .scopes("main") // 配置当前客户端的权限main只是个名字
                .authorizedGrantTypes("password") // 设置客户端登录的方式 password表示用户名密码登录的方式
        ;
    }

    /**
     * 重写方法3: 配置客户端允许使用的功能
     * 这个配置设置资源服务器的安全
     * 规定哪些资源可以被客户端访问,当前项目的资源对所有客户端开放
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()") // 允许哪些客户端访问生成令牌的端点(permitAll():全部允许)
                                                // 如果想允许某个客户端访问必须填写客户端的名字
                .checkTokenAccess("permitAll()")  // 允许哪些客户端访问验证令牌的端点
                .allowFormAuthenticationForClients() //允许通过验证的客户端获得令牌
        ;
    }
}
